Vertex shaders are the part of the pipeline where you
are given control of every vertex that gets processed by the system. In
previous versions of Direct3D, you had the option of using the fixed
function pipeline, which had a built-in set of functionality that it
used when processing vertices. Now with the latest Direct3D, you must do
all the processing yourself. To that end, you’ll need to write at least
a simple vertex shader.
Vertex shaders are one of three shaders that can
exist within an effect file. As objects are sent to be drawn, their
vertices are sent to your vertex shader. If you don’t want to do any
additional processing to the vertices, you can pass them along to the
pixel shader to be drawn. In most cases though, you’ll at least want to
apply a world or projection transform so the vertices are placed in the
proper space to be rendered.
In the following example vertex shader, the
incoming vertex position is multiplied by the projection matrix before
being sent to the pixel shader. You’ll also notice that the vertex color
is left alone and just passed along.
////////////////////////////////////////////////
// Vertex Shader - Main Function
///////////////////////////////////////////////
PS_INPUT VS(float4 Pos : POSITION, float4 Color : COLOR)
{
PS_INPUT psInput;
// Pass through both the position and the color
psInput.Pos = mul( Pos, Projection );
psInput.Color = Color;
return psInput;
}
Using
vertex shaders, you have a lot of power to manipulate the vertices past
just doing a simple transform. The vertex can be translated along any
of the axes, its color changed, or any of its other properties
manipulated. In the next section you’ll be shown how the vertices can be
made to animate within a vertex shader.
Grid Animation in the Shader
Just because you want to move around the vertices
within an object doesn’t mean that you have to do it in your
application code. If the movement can be defined into either a set
pattern or the movement value can be passed in, all the vertex animation
can be done in the shader.
In the following example, the calling program sends in a time value using the TimeStep
variable in addition to the vertices. The time value gives the vertex
shader knowledge of time passing so that the vertices can be changed
each frame.
As each vertex from this object is sent through the vertex shader, a new height position is calculated using the TimeStep variable and the built-in sine function. This results in a wave motion being applied to the grid.
// constant buffer of external variables
cbuffer Variables
{
matrix Projection;
matrix World;
float TimeStep;
};
////////////////////////////////////////////////
// Vertex Shader - Main Function
///////////////////////////////////////////////
PS_INPUT VS(float4 Pos : POSITION, float4 Color : COLOR)
{
PS_INPUT psInput;
float4 newPosition;
newPosition = Pos;
// generate a new height value based on the time
newPosition.y = sin((newPosition.x * TimeStep) + (newPosition.z / 3.0f)) *
5.0f;
// Pass through both the position and the color
psInput.Pos = mul(newPosition, Projection);
psInput.Color = Color;
return psInput;
}
Timing the Animation
As I mentioned before, the animation in the vertex shader is controlled by an updating time value called TimeStep. TimeStep is created and accessed just like the other variables you’ve sent to the shader. First an ID3D10EffectScalarVariable is declared to use as a binding between the application and shader code.
ID3D10EffectScalarVariable* pTimeVariable = NULL;
Next, the pTimeVariable is bound to the TimeStep shader variable.
pTimeVariable = modelObject->pEffect->GetVariableByName("TimeStep")->AsScalar();
Finally, the pTimeVariable is used to update the time value every frame.
// The time is set in the shader using the SetFloat function
pTimeVariable->SetFloat((float)currentTime);
The currentTime value is just a float value that contains the amount of time elapsed between frames.